/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "TerrainBrushManager.h"

#include "TerrainManager.h"
#include "TerrainDecal.h"
#include "GameEngine.h"
#include "GPKArchiveManager.h"

namespace PQEngine
{
	const std::string TerrainBrushManager::ARCHIVE_NAME="TerrainBrush";
	TerrainBrushManager::TerrainBrushManager()
		: _selectedIndex(0), _brushSize(1.0f), _brushIntensity(1.0f), _brushSelected(false)
	{
		_brushData = OGRE_ALLOC_T(float, TerrainManager::BRUSH_DATA_SIZE * TerrainManager::BRUSH_DATA_SIZE, Ogre::MEMCATEGORY_GEOMETRY);
		
		bool success=GNgGPKArchiveManager.hasArchive(ARCHIVE_NAME);
		if(!success){
			_archive=GNgGPKArchiveManager.createArchive(ARCHIVE_NAME);
		}else{
			_archive=GNgGPKArchiveManager.getArchive(ARCHIVE_NAME);
		}

		assert(_archive);
		
		int cnt=_archive->getNumFile();
		for(int i=0;i<cnt;i++){
			std::string name=_archive->getFileName(i);
			_nameList.push_back(name);

			loadTexture(i);
		}

		_decalTexture=Ogre::TextureManager::getSingleton().createManual(
			TerrainDecal::TERRAIN_DECAL_TEXTURE,GameEngine::TERRAIN_RESOURCE_GROUP_NAME,
			Ogre::TEX_TYPE_2D,256,256,1,1,Ogre::PF_A8R8G8B8,Ogre::TU_DYNAMIC_WRITE_ONLY);

		clearBrushTexture();

	}

	void TerrainBrushManager::loadTexture(int index)
	{
		
		GPKFile* file=_archive->getFile(index);
		if(!file||!file->ok()){
			return;
		}

		file->open();
		int size=file->length();
		char* buf=new char[size];
		file->read(buf,size);
		file->close();

		Ogre::DataStreamPtr stream=Ogre::DataStreamPtr(new Ogre::MemoryDataStream(buf,size));

		Ogre::Image img;
		img.load(stream);
		delete buf;

		Ogre::String textureName=_archive->getFileName(index);
		
		Ogre::TextureManager::getSingleton().loadImage(textureName,
			GameEngine::TERRAIN_RESOURCE_GROUP_NAME,
			img);
		
		
	}


	TerrainBrushManager::~TerrainBrushManager()
	{
		 if(_brushData){ 
			OGRE_FREE(_brushData, Ogre::MEMCATEGORY_GEOMETRY);
			_brushData = 0;
		}
		 if(!_decalTexture.isNull()){
			 _decalTexture->unload();
			_decalTexture.setNull();
		}
	}

	void TerrainBrushManager::selectBrush(std::string brush)
	{
		if(!hasBrush(brush)){
			return;
		}

		if(_decalTexture.isNull()){
			return;
		}
		Ogre::Image image;
		image.load(brush,GameEngine::TERRAIN_RESOURCE_GROUP_NAME);
        
		unsigned char *data = OGRE_ALLOC_T(unsigned char, image.getWidth() * image.getHeight() * 3, Ogre::MEMCATEGORY_GEOMETRY);
		Ogre::PixelBox resultbox(image.getWidth(),image.getHeight(),1,Ogre::PF_B8G8R8,data);
		Ogre::PixelUtil::bulkPixelConversion(image.getPixelBox(), resultbox);

		resultbox.setConsecutive();
		int pos = 0;
		for(Ogre::uint i = 0;i < image.getHeight() * image.getWidth();i++){
			data[pos] = 0;
			data[pos+1] = 0;
			data[pos+2] =(unsigned char)((float)data[pos+2] * 0.8f);
			pos += 3;
		}

		_decalTexture->setHeight(image.getHeight());
		_decalTexture->setWidth(image.getWidth());

		Ogre::HardwarePixelBufferSharedPtr ptr = _decalTexture->getBuffer();
		ptr->blitFromMemory(resultbox);
		OGRE_FREE(data, Ogre::MEMCATEGORY_GEOMETRY);

		image.resize((Ogre::ushort)_brushSize, (Ogre::ushort)_brushSize);
		image.resize(TerrainManager::BRUSH_DATA_SIZE, TerrainManager::BRUSH_DATA_SIZE);
		
		setBrushImage(image);
		
		_brushSelected=true;
	}

	void TerrainBrushManager::setBrushImage(Ogre::Image& image)
	{
		int position = 0;
		Ogre::ColourValue value;
		for(Ogre::uint y = 0;y < TerrainManager::BRUSH_DATA_SIZE ;y++){
			position = ((TerrainManager::BRUSH_DATA_SIZE - 1) - y) * TerrainManager::BRUSH_DATA_SIZE;
			for(Ogre::uint x = 0;x < TerrainManager::BRUSH_DATA_SIZE ;x++){
				value = image.getColourAt(x,y,0);
				_brushData[position] = value.r;
				position++;
			}
		}
	}

	void TerrainBrushManager::deselectBrush()
	{
		_brushSelected=false;
		clearBrushTexture();
	}

	void TerrainBrushManager::clearBrushTexture()
	{
		unsigned char *data = OGRE_ALLOC_T(unsigned char, 2 * 2 * 3, Ogre::MEMCATEGORY_GEOMETRY);
		Ogre::PixelBox pixelBox(2,2,1,Ogre::PF_B8G8R8,data);

		pixelBox.setConsecutive();
		int pos = 0;
		for(Ogre::uint i = 0;i <2 * 2;i++){
			data[pos] = 0;
			data[pos+1] = 0;
			data[pos+2] =0;
			pos += 3;
		}

		_decalTexture->setHeight(2);
		_decalTexture->setWidth(2);

		Ogre::HardwarePixelBufferSharedPtr ptr = _decalTexture->getBuffer();
		ptr->blitFromMemory(pixelBox);
		OGRE_FREE(data, Ogre::MEMCATEGORY_GEOMETRY);
	}

	bool TerrainBrushManager::addBrushTextureFromLocalFS(std::string filePath)
	{
		assert(_archive);
		bool success=_archive->addFile(filePath);

		if(success){
			std::string name=NgFileSystem::getPathLastNode(filePath);
			_nameList.push_back(name);
		}
		return success;
	}

	GPKFile* TerrainBrushManager::getTextureFile(int index)
	{
		assert(_archive);
		GPKFile* file=_archive->getFile(index);

		return file;
	}

	GPKFile* TerrainBrushManager::getTextureFile(std::string name)
	{
		assert(_archive);
		GPKFile* file=_archive->getFile(name);

		return file;
	}


	void TerrainBrushManager::selectBrush(int index)
	{
		int size=_nameList.size();
		if(index<0||index>=size||_decalTexture.isNull()){
			return;
		}
		
		std::string brush=_nameList[index];

		Ogre::TexturePtr tex= Ogre::TextureManager::getSingleton().getByName(brush);
		if(tex.isNull()){
			return;
		}
		
		Ogre::Image image;
		tex->convertToImage(image);
		
        
		unsigned char *data = OGRE_ALLOC_T(unsigned char, image.getWidth() * image.getHeight() * 3, Ogre::MEMCATEGORY_GEOMETRY);
		Ogre::PixelBox resultbox(image.getWidth(),image.getHeight(),1,Ogre::PF_B8G8R8,data);
		Ogre::PixelUtil::bulkPixelConversion(image.getPixelBox(), resultbox);

		resultbox.setConsecutive();
		int pos = 0;
		for(Ogre::uint i = 0;i < image.getHeight() * image.getWidth();i++){
			data[pos] = 0;
			data[pos+1] = 0;
			data[pos+2] =(unsigned char)((float)data[pos+2] * 0.8f);
			pos += 3;
		}

		_decalTexture->setHeight(image.getHeight());
		_decalTexture->setWidth(image.getWidth());

		Ogre::HardwarePixelBufferSharedPtr ptr = _decalTexture->getBuffer();
		ptr->blitFromMemory(resultbox);
		OGRE_FREE(data, Ogre::MEMCATEGORY_GEOMETRY);

		image.resize((Ogre::ushort)_brushSize, (Ogre::ushort)_brushSize);
		image.resize(TerrainManager::BRUSH_DATA_SIZE, TerrainManager::BRUSH_DATA_SIZE);
		
		setBrushImage(image);
		
		_brushSelected=true;
	}

}
